﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

using agree.schema;

using alib.Array;
using alib;
using alib.Character;
using alib.String;
using alib.IO;
using alib.Enumerable;
using alib.Concurrency;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Reflection;
using alib.Debugging;
using alib.Tally;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class DerivationSet : IdentIndexedHash<IDerivation>
	{
		//public static readonly DerivationSet Empty = new DerivationSet(null, Collection<IDerivation>.None);

		public DerivationSet(IIdentity prv, IEnumerable<IDerivation> items)
			: base(prv, items)
		{
		}
		public DerivationSet(IIdentity prv, int capacity)
			: base(prv, capacity)
		{
		}
		public DerivationSet(IIdentity prv)
			: this(prv, 13)
		{
		}

		ITallySet<Edge.Flag> _prt;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		ITallySet<Edge.Flag> pred_rel_tallies
		{
			get
			{
				if (_prt == null)
					this._prt = this.SelectMany(irm => irm.DerivationTfs().RelPreds.Distinct()).ToTallies();
				return _prt;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public IEnumerable<Edge.Flag> DistinctivePreds(IDerivation d)
		{
			var tfs = d.DerivationTfs();
			if (tfs == null)
				return Collection<Edge.Flag>.None;
			return tfs.RelPreds.Where(pred => pred_rel_tallies[pred] == 1);
		}

		public IEnumerable<String> DistinctivePredStrings(IDerivation d)
		{
			var tfs = d.DerivationTfs();
			if (tfs == null)
				return Collection<String>.None;
			var tu = tfs.tu;
			return tfs.RelPreds.Where(pred => pred_rel_tallies[pred] == 1).Select(f => tu.TypeNameOrStringValue(f));
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class PackedForest : DerivationSet
	{
		public PackedForest(IChartControl ctrl, out Action a_parse_complete)
			: base(ctrl)
		{
#if DEBUG
			this.ctrl = ctrl;
#endif
			a_parse_complete = set_complete;
		}

		Task<UnpackedForest> t_unpacked;

		bool parse_complete;

		void set_complete() { this.parse_complete = true; }

#if DEBUG
		readonly IChartControl ctrl;

		public override int Add(IDerivation item)
		{
			if (parse_complete)
				throw new Exception();

			var co = item as IChartObj;
			if (co != null)
			{
				Debug.Assert(!co.IsHoldOrRemove());
				Debug.Assert(ctrl.IsSpanning(co));
			}

			return base.Add(item);
		}
#endif

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public UnpackedForest Unpack
		{
			get
			{
				var _tmp = t_unpacked;
				if (_tmp == null)
				{
					var mine = new Task<UnpackedForest>(_unpack_task, (Object)this);
					if (null == (_tmp = Interlocked.CompareExchange(ref t_unpacked, mine, null)))
						(_tmp = mine).Start();
				}
				return _tmp.Result;
			}
		}

		static UnpackedForest _unpack_task(Object _this) { return ((PackedForest)_this).unpack_task(); }

		UnpackedForest unpack_task()
		{
			var rds = new UnpackedForest(this, this.Count);
			foreach (var d in this)
			{
				var po = d as IPacking;
				if (po == null)
					rds.Add(d);
				else
					foreach (var deriv in po.Derivations)
						rds.Add(deriv);
			}
			//Debug.Print("Forest unpack complete. {0} -> {1} roots", this.Count, rds.Count);
			return rds;
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class UnpackedForest : DerivationSet
	{
		public UnpackedForest(IIdentity prv)
			: base(prv)
		{
		}
		public UnpackedForest(IIdentity prv, int capacity)
			: base(prv, capacity)
		{
		}

		Task<ValidatedForest> t_validate;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ValidatedForest ValidateAll
		{
			get
			{
				var _tmp = t_validate;
				if (_tmp == null)
				{
					var mine = new Task<ValidatedForest>(_validate_task, (Object)this);
					if (null == (_tmp = Interlocked.CompareExchange(ref t_validate, mine, null)))
						(_tmp = mine).Start();
				}
				return _tmp.Result;
			}
		}

		static ValidatedForest _validate_task(Object _this) { return ((UnpackedForest)_this).validate_task(); }

		ValidatedForest validate_task()
		{
			var vds = new ValidatedForest(this, this.Count);
			Tfs tfs;

			foreach (var deriv in this)
			{
				if ((tfs = deriv.DerivationTfs()) == null)
					continue;
#if false
				//var gctrl = t0 as GenControl;
				if (gctrl != null)
				{
					var mrsm = tfs.mrsm;
					TfsSlot ts = tfs[mrsm.InitialSemanticsPath];
					TfsSlot[] new_rels = mrsm.GetRelSlots(ts);

					if (new_rels.Length != gc.input.Rels.Length ||
						!gc.fsu.RelMatchHelper.CompareRels(gc.input.ts_mrs, ts, false, false))
					{
						Debug.Print("final semantics check failed");
						continue;
					}
				}
#endif
				vds.Add(deriv);
			}
			//Debug.Print("Forest validation complete. {0} -> {1} roots", this.Count, vds.Count);
			return vds;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class ValidatedForest : DerivationSet
	{
		public ValidatedForest(IIdentity prv)
			: base(prv)
		{
		}
		public ValidatedForest(IIdentity prv, int capacity)
			: base(prv, capacity)
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Readings { [DebuggerStepThrough] get { return this.Count; } }
	};
}
